package bilibili

import (
	"errors"
	"fmt"
	"github.com/gocolly/colly"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/os/gctx"
	"github.com/gogf/gf/v2/text/gregex"
	"github.com/gogf/gf/v2/util/gconv"
	"uni-crawl-frame/core"
	"uni-crawl-frame/service/crawl/vodservice"
	"uni-crawl-frame/utils/constant"
)

type BilibiliCrawl struct {
	*core.AbstractCrawlVodFlow
}

const (
	mp4JsonUrl = "https://api.bilibili.com/x/player/playurl"
)

func (r *BilibiliCrawl) UseBrowser() bool {
	return false
}

func (r *BilibiliCrawl) FillTargetRequest(ctx *core.ApplicationContext) {

	coll := colly.NewCollector()
	coll.OnResponse(func(response *colly.Response) {
		bodyStr := string(response.Body)

		wd, err := getData(bodyStr, ctx)
		if err != nil {
			ctx.Log.Warning(gctx.GetInitCtx(), err.Error())
			switchToVIPMode(ctx)
			return
		}

		innerColl := colly.NewCollector()
		innerColl.OnResponse(func(response *colly.Response) {
			jsonObj := gjson.New(response.Body)
			videoUrl := jsonObj.Get("data.durl.0.url").String()
			ctx.Log.Infof(gctx.GetInitCtx(), "video url = %s", videoUrl)

			if videoUrl == "" {
				// 切换大会员策略
				switchToVIPMode(ctx)
			} else {
				// 免费资源
				ctx.CrawlQueueSeed.CrawlM3U8Url = videoUrl
				ctx.CrawlQueueSeed.CrawlType = vodservice.TypeMP4Url

				params := new(vodservice.CrawlSeedParams)
				params.AddM3U8UrlReqHeader("Referer", ctx.CrawlQueueSeed.CrawlSeedUrl)
				ctx.CrawlQueueSeed.CrawlSeedParams = params.ToJsonString()
			}

		})

		err = innerColl.Visit(fmt.Sprintf("%s?qn=80&avid=%d&cid=%d", mp4JsonUrl, wd.Aid, wd.Cid))
		if err != nil {
			ctx.Log.Error(gctx.GetInitCtx(), err)
			return
		}
	})

	err := coll.Visit(ctx.CrawlQueueSeed.CrawlSeedUrl)
	if err != nil {
		ctx.Log.Error(gctx.GetInitCtx(), err)
		return
	}

}

func switchToVIPMode(ctx *core.ApplicationContext) {
	ctx.RetryFlow = constant.FlowRetry
	ctx.CrawlByBrowserInterface = new(BilibiliVIPCrawl)
}

func getData(bodyStr string, ctx *core.ApplicationContext) (*Data, error) {
	wd := new(Data)
	dataJsons, err := gregex.MatchString(`__INITIAL_STATE__=(.*);\(function\(\)`, bodyStr)
	if len(dataJsons) < 2 || err != nil {
		return nil, errors.New("未从免费的B站资源解析到JSON")
	}

	_ = gconv.Struct(dataJsons[1], wd)

	pid := getPid(ctx.CrawlQueueSeed.CrawlSeedUrl)
	wd.Cid = getCid(wd, pid)

	if wd.Aid == 0 || wd.Cid == 0 {
		return nil, errors.New("未从免费的B站资源解析到aid或cid")
	}

	return wd, err
}

func getCid(wd *Data, pid string) int {
	var cid int
	for _, page := range wd.VideoData.Pages {
		atoi := gconv.Int(pid)
		if page.Page == atoi {
			cid = page.Cid
			break
		}
	}
	return cid
}

func getPid(crawlSeedUrl string) string {
	var pid string
	pidS, err := gregex.MatchString(`\?p=(\d+)`, crawlSeedUrl)
	if err != nil || len(pidS) != 2 {
		pid = "1"
	} else {
		pid = pidS[1]
	}
	return pid
}
